Add Vim9Script support#205
Conversation
Parse core Vim9Script constructs into proper AST nodes so that downstream tools can analyze Vim9Script code without errors. New node types: NODE_VIM9SCRIPT, NODE_DEF, NODE_ENDDEF, NODE_VAR, NODE_FINAL, NODE_EXPORT, NODE_IMPORT. Parsers for vim9script, def/enddef (with typed parameters, return types, default values, variadics), var/final (with type annotations), export (wrapping inner commands), and import. The # comment syntax is supported when vim9script mode is active. Also fix JS transpiler heuristic for subscript assignments in compile_let to check for [ in addition to . when deciding whether to add var prefix. Fixes vim-jp#194
|
I started on this before seeing #204. The approach in that PR is clearly a much better solution than this. (I hadn’t even known yet about https://github.com/vim-jp/vim-vim9parser.) But I figured I’d go ahead and raise this anyway — in case it happens to be of any interest, or might be a possible interim solution until #204 gets merged. |
From a fork of vimlparser that adds basic support for vim9script See vim-jp/vim-vimlparser#205 And https://github.com/sideshowbarker/vim-vimlparser/tree/vim9script-support While not perfect, this is a great improvement, thanks @sideshowbarker A more comprehensive solution is under development, but not yet ready See vim-jp/vim-vimlparser#204 And https://github.com/vim-jp/vim-vim9parser
From a fork of vimlparser that adds basic support for vim9script See vim-jp/vim-vimlparser#205 And https://github.com/sideshowbarker/vim-vimlparser/tree/vim9script-support While not perfect, this is a great improvement, thanks @sideshowbarker A more comprehensive solution is under development, but not yet ready See vim-jp/vim-vimlparser#204 And https://github.com/vim-jp/vim-vim9parser
|
@ynkdir @mattn Can you please have a look at this? The vim9script support would be of a great help! There is a new lsp client being written in it and it would be easier to develop, if there was proper LSP support! |
- Read :def parameter and :var/:final variable names with read_word(), not read_alpha() — so digits and underscores in names are accepted. - Capture read_type() return values for :def parameters into varnode.type_str — so the parsed type is preserved on the AST, rather than getting discarded. - Require whitespace immediately before # in separate_nextcmd — to match Vim9 inline-comment rules. - Strip a trailing comment from :import lines — so the comment text is no longer captured in the import spec. (Implemented with a streaming reader rather than substitute()/slicing — so the Python and JavaScript transpilers can translate the code without new helpers.) - Emit type_str in compile_var and compile_final output when present — so types in declarations like “var count: number = 42” round-trip through the S-expression form as (var = count: number 42). - Rename the VAR/FINAL doc-comment fields from .type to .type_str — so they match the actual node field names.
|
@mattn Thanks~ I’ve pushed a follow-up commit to the branch that I think resolves all your review comments. Lemme know if I missed anything. |
|
@mattn How did you test this? I tried exchanging the |
Yes. And I don’t see any way to make anything else be displayed yet — unless the consuming LSP source is also updated. We’d first need to emit the parameter types in the That said, I’d still be happy to go ahead and implement something for it now. We’d just not yet be able to end-to-end test/observe the expected end-user result. |
|
@jclsn A simple file swap in the installed To test this PR end-to-end, I rebuilt vim-language-server from source against this branch: # 1. Get vim-language-server source
git clone https://github.com/iamcco/vim-language-server
cd vim-language-server
# 2. Replace the vendored parser with this PR's build
cp /path/to/vim-vimlparser/js/vimlparser.js src/lib/vimparser.js
# 3. Rebuild the bundle
npm install
NODE_OPTIONS=--openssl-legacy-provider npm run build
# 4. Drop the rebuilt out/*.js over your installed copy
cp out/*.js "$(npm root -g)/vim-language-server/out/"With that, opening a What you're noticing (no hover, no jump-to-definition on |
|
Thanks for offering. I agree — let's defer that to a follow-up PR. Without vim-language-server consuming the type information, emitting it now would be speculative, and the shape of the output is easier to design once there's a concrete consumer to match. Let's keep this PR focused on the parser additions and merge it as-is. |
|
Thank you |
|
Great to see this merged ❤️ I'll update my |
PR to add support for vim9script to vimlparser has been merged, see vim-jp/vim-vimlparser#205 There were some additional changes in response to PR review

Summary
parse_cmd_common, so downstream tools can analyze Vim9Script code without errors#comment support whenvim9scriptmode is activecompile_letheuristic that incorrectly addedvarprefix to subscript assignments likenamed[pname] = 1Motivation
Tools that depend on
vim-vimlparser— such as vint and vim-language-server (which vendors the JS output directly) — report unexpected errors on valid Vim9Script code because the parser doesn't recognize constructs likedef, typed parameters, or#comments. This adds structured parsing for the core Vim9Script syntax so these tools can handle Vim9Script files correctly.Details
New node types:
NODE_VIM9SCRIPT,NODE_DEF,NODE_ENDDEF,NODE_VAR,NODE_FINAL,NODE_EXPORT,NODE_IMPORT.vim9script— sets a mode flag that enables#comments throughout the parser (inparse_one_cmd,parse_command,parse_comment,parse_trail,ends_excmds,separate_nextcmd).def/enddef— parses typed parameters (name: type), default values (name: type = expr), variadic params (...name: type), and return type annotations (: returntype). Reads parameter names directly from the reader rather than usingExprTokenizer, since:is a valid name character in VimL identifiers and would causex:to be tokenized as a single identifier.var/final— parses variable declarations with optional type annotations. Same direct-reader approach to avoid the:tokenization issue.export— wraps the inner command (def, var, etc.) by delegating tofind_command+_parse_command, then moving the resulting nodes into the export node's body.import— captures the import specification as a string.Fixes #194